/*
 * $Id: SysCodeService.java 21 2014-09-01 04:28:35Z ratking $
 *
 * Copyleft (C) 2014 RatKing. All wrongs reserved.
 */
package cn.ratking.demo.jaws7.system.service;

import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.Stateless;
import javax.enterprise.event.Event;
import javax.inject.Inject;
import javax.persistence.EntityManager;

import org.fluttercode.datavalve.DefaultPaginator;
import org.fluttercode.datavalve.Paginator;
import org.fluttercode.datavalve.el.ExpressionParameterResolver;
import org.fluttercode.datavalve.provider.jpa.JpaQueryProvider;
import org.primefaces.model.SortOrder;
import org.slf4j.Logger;

import cn.ratking.demo.jaws7.common.Constants;
import cn.ratking.demo.jaws7.model.SysCode;
import cn.ratking.demo.jaws7.resource.JawsPU;
import cn.ratking.demo.jaws7.system.vo.SysCodeSearchCriteria;

/**
 * 系统代码服务
 */
@Stateless
public class SysCodeService {

    @Inject
    private transient Logger log;
    @Inject
    @JawsPU
    private EntityManager em;
    /** 事件源 */
    @Inject
    private Event<SysCode> eventSrc;

    @PostConstruct
    public void init() {
        log.debug("Debug: SysCodeService.init()");
    }

    @PreDestroy
    public void destroy() {
        log.debug("Debug: SysCodeService.destroy()");
    }

    /**
     * 创建系统代码
     *
     * @param sysCode 系统代码
     */
    public void create(SysCode sysCode) {
        if (sysCode.getCreateTime() == null) {
            // 设置创建时间
            sysCode.setCreateTime(new Date());
        }
        if (sysCode.getUpdateTime() == null) {
            // 设置更新时间
            sysCode.setUpdateTime(sysCode.getCreateTime());
        }
        em.persist(sysCode);
        // 事件生产者
        eventSrc.fire(sysCode);
    }

    /**
     * 更新系统代码
     *
     * @param sysCode 系统代码
     */
    public void update(SysCode sysCode) {
        // 设置更新时间
        sysCode.setUpdateTime(new Date());
        em.merge(sysCode);
        // 事件生产者
        eventSrc.fire(sysCode);
    }

    /**
     * 分离实体
     *
     * @param sysCode
     */
    public void detach(SysCode sysCode) {
        em.detach(sysCode);
    }

    /**
     * 按ID删除系统代码
     *
     * @param sysCodeId 系统代码ID
     */
    public void deleteById(Long sysCodeId) {
        String ql = "DELETE FROM SysCode t"
                + " WHERE t.sysCodeId = :sysCodeId";
        em.createQuery(ql)
                .setParameter("sysCodeId", sysCodeId)
                .executeUpdate();
        // 事件生产者
        SysCode sysCode = new SysCode();
        sysCode.setSysCodeId(sysCodeId);
        eventSrc.fire(sysCode);
    }

    /**
     * 批量删除系统代码
     *
     * @param sysCodeIdList 系统代码ID的列表
     */
    public void batchDelete(List<Long> sysCodeIdList) {
        String ql = "DELETE FROM SysCode t"
                + " WHERE t.sysCodeId IN :sysCodeIdList";
        em.createQuery(ql)
                .setParameter("sysCodeIdList", sysCodeIdList)
                .executeUpdate();
        // 事件生产者
        for (Long sysCodeId : sysCodeIdList) {
            SysCode sysCode = new SysCode();
            sysCode.setSysCodeId(sysCodeId);
            eventSrc.fire(sysCode);
        }
    }

    /**
     * 按ID查询系统代码
     *
     * @param sysCodeId 系统代码ID
     * @return 系统代码(找不到时返回null)
     */
    public SysCode findById(Long sysCodeId) {
        return em.find(SysCode.class, sysCodeId);
    }

    /**
     * 按代码类型查询系统代码(仅查有效的)
     *
     * @param codeType 代码类型
     * @return 系统代码列表
     */
    public List<SysCode> findByCodeType(String codeType) {
        String ql = "SELECT t"
                + " FROM SysCode t"
                + " WHERE t.codeType = :codeType"
                + " AND t.enabled = :enabled"
                + " ORDER BY t.orderNo";
        List<SysCode> resultList = em.createQuery(ql, SysCode.class)
                .setParameter("codeType", codeType)
                .setParameter("enabled", Boolean.TRUE)
                .getResultList();
        return resultList;
    }

    /**
     * 按代码类型和代码名称查询系统代码
     *
     * @param codeType 代码类型
     * @param codeName 代码名称
     * @return 系统代码(找不到时返回null)
     */
    public SysCode findByCodeTypeAndCodeName(String codeType, String codeName) {
        String ql = "SELECT t"
                + " FROM SysCode t"
                + " WHERE t.codeType = :codeType"
                + " AND t.codeName = :codeName";
        List<SysCode> resultList = em.createQuery(ql, SysCode.class)
                .setParameter("codeType", codeType)
                .setParameter("codeName", codeName)
                .getResultList();
        return resultList.isEmpty() ? null : resultList.get(0);
    }

    /**
     * 按代码类型和代码值查询系统代码
     *
     * @param codeType 代码类型
     * @param codeValue 代码值
     * @return 系统代码(找不到时返回null)
     */
    public SysCode findByCodeTypeAndCodeValue(String codeType, String codeValue) {
        String ql = "SELECT t"
                + " FROM SysCode t"
                + " WHERE t.codeType = :codeType"
                + " AND t.codeValue = :codeValue";
        List<SysCode> resultList = em.createQuery(ql, SysCode.class)
                .setParameter("codeType", codeType)
                .setParameter("codeValue", codeValue)
                .getResultList();
        return resultList.isEmpty() ? null : resultList.get(0);
    }

    /**
     * 重置代码类型
     */
    public void resetCodeType() {
        // 查询旧的代码类型
        List<SysCode> sysCodeList = this.findByCodeType(Constants.CODETYPE_DAIMALEIXING);
        Set<String> codeTypeSet = new HashSet<String>();
        int maxOrderNo = 0;
        for (SysCode sysCode : sysCodeList) {
            codeTypeSet.add(sysCode.getCodeValue());
            if (sysCode.getOrderNo() != null && maxOrderNo < sysCode.getOrderNo()) {
                maxOrderNo = sysCode.getOrderNo();
            }
        }

        if (codeTypeSet.isEmpty()) {
            // 创建一个代码类型为“代码类型”的记录
            SysCode sysCode = new SysCode();
            sysCode.setCodeType(Constants.CODETYPE_DAIMALEIXING);
            sysCode.setCodeName(Constants.CODETYPE_DAIMALEIXING);
            sysCode.setCodeValue(Constants.CODETYPE_DAIMALEIXING);
            sysCode.setOrderNo(++maxOrderNo);
            sysCode.setEnabled(true);
            sysCode.setRemark(Constants.CODETYPE_DAIMALEIXING);
            this.create(sysCode);
            codeTypeSet.add(Constants.CODETYPE_DAIMALEIXING);
        }

        String ql = "SELECT distinct(t.codeType)"
                + " FROM SysCode t"
                + " ORDER BY t.codeType";
        List<String> codeTypeList = em.createQuery(ql, String.class)
                .getResultList();
        for (String codeType : codeTypeList) {
            if (!codeTypeSet.contains(codeType)) {
                SysCode sysCode = new SysCode();
                sysCode.setCodeType(Constants.CODETYPE_DAIMALEIXING);
                sysCode.setCodeName(codeType);
                sysCode.setCodeValue(codeType);
                sysCode.setOrderNo(++maxOrderNo);
                sysCode.setEnabled(true);
                sysCode.setRemark(codeType);
                this.create(sysCode);
            }
        }
    }

    /**
     * 系统代码导入表T_SysCode中.
     * 重复记录会被更新或丢弃
     *
     * @param sysCodeList
     * @return 插入记录数、更新记录数、重复记录数、错误记录数
     */
    public int[] importSysCode(List<SysCode> sysCodeList) {
        // 插入记录数
        int insertCount = 0;
        // 更新记录数
        int updateCount = 0;
        // 重复记录数
        int duplicateCount = 0;
        // 错误记录数
        int errorCount = 0;
        for (SysCode sysCode : sysCodeList) {
            // 与数据库中已存在的记录进行逐条对比，只插入新的，或对旧的更新。更新时，保留历史痕迹
            // 代码类型
            String codeType = sysCode.getCodeType();
            // 代码名称
            String codeName = sysCode.getCodeName();
            // 代码值
            String codeValue = sysCode.getCodeValue();
            // 是否有效(N-无效 Y-有效)
            boolean enabled = sysCode.getEnabled();
            // 排序序号
            Integer orderNo = sysCode.getOrderNo();

            // 查找已存在的系统代码
            SysCode oldSysCode = this.findByCodeTypeAndCodeName(codeType, codeName);
            if (oldSysCode == null) {
                // 插入新记录
                this.create(sysCode);
                insertCount++;
                log.debug("Debug: 插入记录：" + sysCode);
            } else if (!codeValue.equals(oldSysCode.getCodeValue())
                    || enabled != oldSysCode.getEnabled()
                    || (orderNo != null && !orderNo.equals(oldSysCode.getOrderNo()))) {
                oldSysCode.setCodeValue(codeValue);
                oldSysCode.setEnabled(enabled);
                oldSysCode.setOrderNo(orderNo);
                // 更新旧记录
                this.update(oldSysCode);
                updateCount++;
                log.debug("Debug: 更新记录：" + oldSysCode);
            } else {
                duplicateCount++;
                log.debug("Debug: 重复记录：" + sysCode);
            } // if-else if
        } // for
        // 返回插入记录数、更新记录数、重复记录数、错误记录数
        return new int[]{insertCount, updateCount, duplicateCount, errorCount};
    }

    /**
     * 获得符合查询条件的当前页结果列表
     *
     * @param first 第一条记录(从0开始)
     * @param pageSize 每页记录数
     * @param sortField 排序字段
     * @param sortOrder 排序方向
     * @param filters 过滤条件
     * @param searchCriteria 查询条件
     * @return 结果列表
     */
    public List<SysCode> getResultList(int first, int pageSize,
            String sortField, SortOrder sortOrder,
            SysCodeSearchCriteria searchCriteria) {
        // 查询提供器
        JpaQueryProvider<SysCode> provider = new JpaQueryProvider<SysCode>();
        // 设置实体管理器
        provider.setEntityManager(em);
        // 设置实体类型与别名
        provider.init(SysCode.class, "t");
        // 初始化排序字段映射，可避免恶意SQL注入
        provider.getOrderKeyMap().put("sysCodeId", "t.sysCodeId");
        provider.getOrderKeyMap().put("codeType", "t.codeType");
        provider.getOrderKeyMap().put("codeName", "t.codeName");
        provider.getOrderKeyMap().put("codeValue", "t.codeValue");
        provider.getOrderKeyMap().put("enabled", "t.enabled");
        provider.getOrderKeyMap().put("orderNo", "t.codeType, t.orderNo");
        provider.getOrderKeyMap().put("createTime", "t.createTime");
        provider.getOrderKeyMap().put("updateTime", "t.updateTime");
        // 设置表达式参数解析器
        provider.addParameterResolver(new ExpressionParameterResolver());
        // 设置分页与排序
        Paginator paginator = new DefaultPaginator();
        paginator.setFirstResult(first);
        paginator.setMaxRows(pageSize);
        if (sortField == null) {
            // 设置默认排序字段
            sortField = "orderNo";
        }
        paginator.setOrderKey(sortField);
        paginator.setOrderAscending(sortOrder == SortOrder.ASCENDING);
        // 设置查询约束
        setRestrictions(provider, searchCriteria);
        // 返回查询结果
        return provider.fetchResults(paginator);
    }

    /**
     * 获得符合查询条件的记录总数
     *
     * @param searchCriteria 查询条件
     * @return 记录总数
     */
    public int getRowCount(SysCodeSearchCriteria searchCriteria) {
        // 查询提供器
        JpaQueryProvider<SysCode> provider = new JpaQueryProvider<SysCode>();
        // 设置实体管理器
        provider.setEntityManager(em);
        // 设置实体类型与别名
        provider.init(SysCode.class, "t");
        // 设置表达式参数解析器
        provider.addParameterResolver(new ExpressionParameterResolver());
        // 设置查询约束
        setRestrictions(provider, searchCriteria);
        // 返回查询结果
        return provider.fetchResultCount();
    }

    /**
     * 设置查询约束
     *
     * @param queryProvider 查询提供器
     * @param searchCriteria 查询条件
     */
    private void setRestrictions(JpaQueryProvider<SysCode> queryProvider,
            SysCodeSearchCriteria searchCriteria) {
        queryProvider.getRestrictions().clear();

        queryProvider.addRestrictionStr("t.codeType = :param", searchCriteria.getCodeType());
        queryProvider.addRestrictionStr("t.codeName like :param",
                "%" + searchCriteria.getCodeName() + "%",
                searchCriteria.getCodeName());
        queryProvider.addRestrictionStr("t.codeValue like :param",
                "%" + searchCriteria.getCodeValue() + "%",
                searchCriteria.getCodeValue());
    }
}
